package com.sunbird.crm.base.configurate.shiro;

import com.alibaba.fastjson.JSON;
import com.sunbird.crm.entity.sys.TUser;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import sun.security.util.Cache;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @ClassName KickoutSessionControlFilter
 * @Descriptiom TODO
 * @Author Sunbird
 * @Date 2019/8/16 21:16
 * @Version 1.0
 **/
public class KickoutSessionControlFilter extends AccessControlFilter {
    private String kickoutUrl;// 踢出去得到的地址
    private boolean kickouAfter = false ;// 踢出去之前登录的/之后登录的用户 默认踢出去之前的用户
    private  int maxSession =1;//同时账号最大会话数 1
    private SessionManager sessionManager;
    private Cache<String, Deque<Serializable>> cache;

    public String getKickoutUrl() {
        return kickoutUrl;
    }

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public boolean isKickouAfter() {
        return kickouAfter;
    }

    public void setKickouAfter(boolean kickouAfter) {
        this.kickouAfter = kickouAfter;
    }

    public int getMaxSession() {
        return maxSession;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public SessionManager getSessionManager() {
        return sessionManager;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public Cache<String, Deque<Serializable>> getCache() {
        return cache;
    }

    public void setCacheManager(CacheManager cache) {
        this.cache = (Cache<String, Deque<Serializable>>) cache.getCache("shiro_redis_cache");
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        Subject subject = getSubject(servletRequest,servletResponse);
        if (!subject.isAuthenticated() && !subject.isRemembered()){
            return  true;
        }
        Session session =subject.getSession();
        TUser user = (TUser) subject.getPrincipal();
        String usernam =user.getUserName();
        Serializable sessionId = session.getId();

        //读取缓存  没有就存入
        Deque<Serializable> deque = cache.get(usernam);
        //如果此用户没有seesion 列队，也就是没有登录，缓存中没有
        //new 一个新的列队 ，不然 deque 对象为空，会报空指针
        if(deque == null){
            deque = new LinkedList<Serializable>();
        }
        // 如果列队里没有此sessionId ,并且用户没有被踢出；放入列队
        if (!deque.contains(sessionId) && session.getAttribute("kicout")== null){
            //将sessionId 存入列队
            deque.push(sessionId);
            cache.put(usernam,deque);
        }
        while (deque.size() > maxSession){
            Serializable kickoutSessionId =null;
            if (kickouAfter ){
                //踢出后者登录的
                kickoutSessionId =deque.removeFirst();
                cache.put(usernam,deque);
            }else {//踢出前者
                kickoutSessionId = deque.removeLast();
                cache.put(usernam,deque);
            }

            try {
                //获取被踢出的对象 session
                Session kickoutSession =sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if (kickoutSession != null){
                    // 设置会话的kickout 属性表示踢出
                    kickoutSession.setAttribute("kickout",true);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        //如果被踢出。直接退出，重新定向到踢出页面
        if(session.getAttribute("kickout") != null){
            try{
                subject.logout();
            }catch (Exception e){

            }
            saveRequest(servletRequest);

            Map<String,String> resultMap = new HashMap<String, String>();

            //判断是否是Ajax 请求
            if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) servletRequest).getHeader("X-Requested-With"))){
                    resultMap.put("user_status","300");
                    resultMap.put("message","你账号在其它地方登录");
               out(servletResponse,resultMap);
            }else {
                WebUtils.issueRedirect(servletRequest,servletResponse,kickoutUrl);
            }
            }

        return false;
    }

    private void out(ServletResponse hresponse, Map<String, String> resultMap)
            throws IOException {
        try {
            hresponse.setCharacterEncoding("UTF-8");
            PrintWriter out = hresponse.getWriter();
            out.println(JSON.toJSONString(resultMap));
            out.flush();
            out.close();
        } catch (Exception e) {
            System.err.println("KickoutSessionFilter.class 输出JSON异常，可以忽略。");
        }
    }
}
